﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.TestManagement.Client;
using Microsoft.TeamFoundation;

using TFhisto.Colors;

namespace TFhisto
{
    public class ProcessFullAnalysis
    {
        #region private members
        private Dictionary<int, WorkItemStub> _workItemStubs;  //Stores the distinct list of all work items to be written to the dgml
        private Dictionary<int, ChangesetStub> _changesetStubs;  //Stores the distinct list of all changesets to be written to the dgml
        private Dictionary<int, LabelStub> _labelStubs;  //Stores the distinct list of all labels to be written to the dgml
        private WorkItemStore _workItemStore;
        private VersionControlServer _vcs;
        private ITestManagementService _testManagementService;
        private ITestManagementTeamProject _testManagementTeamProject;

        private List<string> _groupByDistinctValues = new List<string>();   //Stores a distinct list of values for which we are grouping (if we are)
        private Dictionary<string, FileChangeInfo> _allFiles = new Dictionary<string, FileChangeInfo>();//Stores a distinct list of files
        private List<TempLinkType2> _links;         //Stores all link types

        private string _projectName;                //The name of the team project
        private string _outputFile;                 //The DGML file to write to        

        private string _labelPattern = string.Empty;
        private DateTime _searchFirstDate;
        private DateTime _searchLastDate;
        private int _searchFirstChangesetId;
        private int _searchLastChangesetId;

        private string _groupByFieldReferenceName = null;    // The field reference name (if any) for which we are doing a grouping
        private string _groupByFieldName;           // the field name (if any) that we are grouping by. This is the label that will appear on the group

        private bool _dependencyAnalysis = false;   //Are we doing a dependency analysis?
        private bool _hideLinks = false;            //If we are doing a dependency analysis, should we hide the intervening nodes and links
        private bool _showDeletedLinks = false;     //Deleted links are always stored by TFS, we propose the possibility to show/hide those deleted links
        private bool _showFiles = false;            //Do we show files in changesets
        private bool _full;                         //Did the user select full processing
        private List<string> _allNodeTypes;         //Stores a distinct list of all node types for use in styling the nodes
        private List<string> _allWorkitemTypes;     //Stores a distinct list of all workitem types for use in styling the nodes
        private List<string> _allChangeTypes;         //Stores a distinct list of all change types for use in styling the links

        private Dictionary<string, TempLinkType> _selectedLinks; //This is the list of links that the user selected on the UI. Indexed by the LinkType 
        private WorkItemStub _tempStub;             //Used for dependency analysis to hold the stub which contains the changeset(s)

        private Dictionary<int, object> _visitedChangeSetsForWorkItem; // list to guarantee that we only process the changesets for a given WI once
        private Dictionary<string, object> _visitedServerPath;         // list to guarantee that we only process a filepath once
        private Dictionary<int, TestPlanInfo> _testPlans = new Dictionary<int, TestPlanInfo>(); //Stores a distinct list of test plans

        /// <summary>
        /// Stores whether MTM hyperlinks or TWA hyperlinks will be used
        /// Note that TWA hyperlinks are always used for changesets
        /// MTM hyperlinks are always used for test results, test plans and test runs
        /// So this only affects work items right now
        /// </summary>
        private bool _useMtmLinks = true;
        #endregion

        public ProcessFullAnalysis(WorkItemStore wis, VersionControlServer vcs,
            string projectName, string outputFile, bool useMtmLinks)
        {
            _workItemStore = wis;
            _vcs = vcs;
            _testManagementService = vcs.TeamProjectCollection.GetService<ITestManagementService>();
            _testManagementTeamProject = _testManagementService.GetTeamProject(projectName);
            _projectName = projectName;
            _outputFile = outputFile;
            _useMtmLinks = useMtmLinks;
        }

        #region Process Work Items and Changesets
        /// <summary>
        /// Method for processing work items down to the changesets that are related to them
        /// </summary>
        /// <param name="wi">Work Item to process</param>
        /// <param name="outputFile">File to write the dgml to</param>
        /// <param name="vcs">Version Control Server which contains the changesets</param>
        public void ProcessWorkItemRelationships()
        {
            _full = true;

            //Pull every work item for the project
            Query q = new Query(_workItemStore, string.Format("SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], "
                + "[System.State] FROM WorkItems WHERE [System.TeamProject] = '{0}'", _projectName));
            WorkItemCollection wic = q.RunQuery();

            WorkItem[] workItems = new WorkItem[wic.Count];

            for (int i = 0; i < wic.Count; i++)
                workItems[i] = wic[i];

            Process(workItems);
        }

        public void ProcessWorkItemRelationships(WorkItem[] wi, string groupByFieldReferenceName, bool dependencyAnalysis, List<TempLinkType> selectedLinks, bool hideLinks, bool showDeletedLinks)
        {
            _full = false;
            _groupByFieldReferenceName = groupByFieldReferenceName;

            if (String.IsNullOrEmpty(_groupByFieldReferenceName) == false)
            {
                if (wi[0].Store.FieldDefinitions[_groupByFieldReferenceName] == null)
                {
                    _groupByFieldReferenceName = null;
                }
                else
                {
                    _groupByFieldName = wi[0].Store.FieldDefinitions[_groupByFieldReferenceName].Name;
                }
            }

            _dependencyAnalysis = dependencyAnalysis;
            _selectedLinks = GetSelectedLinksFromUI(selectedLinks);
            _hideLinks = hideLinks;
            _showDeletedLinks = showDeletedLinks;

            Process(wi);
        }

        public void ProcessChangesetRelationships(SearchHelper searchHelper,
            string labelPattern,
            List<FileChangeInfo> selectedFiles,
            BranchObject[] br,
            string groupByFieldReferenceName,
            List<TempLinkType> selectedLinks,
            bool showFiles,
            bool groupFiles,
            bool showLabels,
            bool showComments,
            bool showWI)
        {
            _full = false;

            //TODO for the moment we only support Group By Branch
            _groupByFieldReferenceName = groupByFieldReferenceName;
            if (_groupByFieldReferenceName == null || _groupByFieldReferenceName.Equals(string.Empty))
            {
                _groupByFieldReferenceName = "Branch";
            }

            _showFiles = showFiles;
            _selectedLinks = GetSelectedLinksFromUI(selectedLinks);

            Process(searchHelper, labelPattern, selectedFiles, br, showFiles, groupFiles, showLabels, showComments, showWI);
        }

        /// <summary>
        /// Gets an indexed list (by the LinkType name) list of selected link types
        /// </summary>
        /// <param name="selectedLinks"></param>
        /// <returns></returns>
        private Dictionary<string, TempLinkType> GetSelectedLinksFromUI(List<TempLinkType> selectedLinks)
        {
            Dictionary<string, TempLinkType> links;

            links = new Dictionary<string, TempLinkType>();

            foreach (TempLinkType linkType in selectedLinks)
            {
                links.Add(linkType.LinkType, linkType);
                //we add the lower version used by Change elements
                links.Add(linkType.LinkType.ToLower(), linkType);
            }

            return links;
        }

        private void Process(WorkItem[] workItems)
        {
            _workItemStubs = new Dictionary<int, WorkItemStub>();
            _allNodeTypes = new List<string>();

            _visitedChangeSetsForWorkItem = new Dictionary<int, object>();
            _visitedServerPath = new Dictionary<string, object>();

            //Get the full list of link types for assigning category relationships
            _links = LoadLinkTypes(_projectName, false);

            //Process all work items
            for (int i = 0; i < workItems.Length; i++)
            {
                ProcessWorkItemCS(workItems[i]);
            }

            //The colors for all other node types (i.e. not work item types)
            _allNodeTypes.Add("Changeset");
            _allNodeTypes.Add("Iteration");
            _allNodeTypes.Add("File");
            _allNodeTypes.Add("Test Plan");
            _allNodeTypes.Add("Test Run");
            _allNodeTypes.Add("Test Result");

            //Process the changesets so they are also related to the actual root work item
            if (_dependencyAnalysis)
            {
                //Only process work items that have changesets
                for (int i = 0; i < _workItemStubs.Count; i++)
                {
                    if (_workItemStubs[i].Changesets.Count > 0)
                    {
                        _tempStub = _workItemStubs[i];
                        GetWorkItemParent(_tempStub);
                    }
                }
            }

            //Output the results
            WriteWorkItemInfo(_outputFile, _projectName);
        }

        private void Process(SearchHelper searchHelper
                                , string labelPattern
                                , List<FileChangeInfo> selectedFiles
                                , BranchObject[] selectedBranches
                                , bool showFiles
                                , bool groupFiles
                                , bool showLabels
                                , bool showComments
                                , bool showWI)
        {
            _workItemStubs = new Dictionary<int, WorkItemStub>();
            _allNodeTypes = new List<string>();
            _allWorkitemTypes = new List<string>();
            _allChangeTypes = new List<string>();

            _visitedChangeSetsForWorkItem = new Dictionary<int, object>();
            _visitedServerPath = new Dictionary<string, object>();

            //Get the full list of link types for assigning category relationships
            _links = LoadLinkTypes(_projectName, true);

            _labelPattern = labelPattern;
            _searchFirstDate = searchHelper.GetDateFrom();
            _searchLastDate = searchHelper.GetDateTo();
            _searchFirstChangesetId = searchHelper.GetChangesetFrom();
            _searchLastChangesetId = searchHelper.GetChangesetTo();

            VersionControlServer vcs = CommonUIHelper.Helper._tfs.GetService<VersionControlServer>();

            System.Collections.Generic.List<BaseListItem> historList = new System.Collections.Generic.List<BaseListItem>();

            foreach (FileChangeInfo file in selectedFiles)
            {
                if (file.Selected)
                {
                    string serverItem = file.FullPath;

                    //We first get the first/root Changeset considering all the branches connected with the current branch
                    int changesetId = GetRootChangesetId(ref serverItem, vcs);

                    //We log all the Branches, to enable Index and quick search in future operations.
                    ItemSpec[] itemSpecArray = new ItemSpec[1] { new ItemSpec(serverItem, RecursionType.Full, 0) };
                    ChangesetVersionSpec changesetVersionSpec = new ChangesetVersionSpec(changesetId);
                    BranchHistoryTreeItem[][] branchHistoryTreeItemArrayOfArrays = vcs.GetBranchHistory(itemSpecArray, changesetVersionSpec);
                    CommonUIHelper.RegisterBranchItem(vcs.GetBranchHistory(itemSpecArray, changesetVersionSpec)[0][0]);

                    //We can now rebuild the full history 
                    GetBranchHistory(serverItem, changesetId, 0, historList);

                    _changesetStubs = new Dictionary<int, ChangesetStub>();
                    _labelStubs = new Dictionary<int, LabelStub>();
                    ProcessCSHistory(historList);

                    // fermer ici le foreach
                    // tester l'impact....
                }
            }

            CommonUIHelper.ReorganizeMergeDictionary();

            if (showFiles)
            {
                foreach (ChangesetStub stub in _changesetStubs.Values)
                {
                    foreach (FileChangeInfo change in stub.Files)
                    {
                        if (!_allNodeTypes.Contains(change.ChangeType))
                        {
                            _allNodeTypes.Add(change.ChangeType);
                        }
                    }
                }
            }
            else
            {
                foreach (ChangesetStub stub in _changesetStubs.Values)
                {
                    if (!_allNodeTypes.Contains(stub.ChangeToFile))
                    {
                        _allNodeTypes.Add(stub.ChangeToFile);
                    }
                }
            }

            //Output the results
            WriteChangesetInfo(_outputFile, _projectName, showFiles, groupFiles, showLabels, showComments, showWI);
        }

        /// <summary>
        /// Finds the link type based on an end name
        /// </summary>
        /// <param name="value">The end to use in the search (such as Parent)</param>
        /// <returns>The name of the relationship (such as Parent/Child)</returns>
        private TempLinkType2 GetRelationship(string value)
        {
            return _links.Find(stub => { return stub.Reverse == value || stub.Forward == value; });
        }

        private bool ConsiderChangeset(int changesetId)
        {
            bool result = true;

            if ((_searchFirstChangesetId > 0 && changesetId < _searchFirstChangesetId) || (_searchLastChangesetId > 0 && changesetId > _searchLastChangesetId))
            {
                result = false;
            }
            return result;
        }

        /// <summary>
        /// Write the changeset and associated Work Items information to a dgml file
        /// </summary>
        /// <param name="outputFile">The file to write the dgml to</param>
        /// <param name="project">The name of the team project</param>
        /// <param name="useMtm">Indicates whether to use MTM hyperlinks (true) or TWA hyperlinks (false)</param>
        private void WriteChangesetInfo(string outputFile, string project, bool showFiles, bool groupFiles, bool ShowLabels, bool showComments, bool showWI)
        {
            IColorConfiguration colorConfiguration;
            Dictionary<string, string> properties = null;
            //List<int> blackList = new List<int>();

            //Create a new DGML document
            XmlTextWriter xtw = XmlHelper.CreateHeader(outputFile);

            #region Write Nodes
            //Add the nodes section
            xtw.WriteStartElement("Nodes");

            //These two arrays are used to store the single category for both files and changesets
            string[] cs = new string[] { "Has Work Items/Has Changesets" };
            string[] file = new string[] { "In Changeset/Has Files", "Associated Work Items/Has Files" };

            // add the group by values if we are grouping by
            if (String.IsNullOrEmpty(_groupByFieldReferenceName) == false)
            {
                for (int i = 0; i < _groupByDistinctValues.Count; i++)
                {
                    XmlHelper.WriteXMLNode(true, true, xtw, _groupByDistinctValues[i], _groupByDistinctValues[i], _groupByFieldName, "Expanded");
                }
            }

            //Add the individual changeset nodes
            foreach (ChangesetStub changesetStub in _changesetStubs.Values)
            {
                //if (ConsiderChangeset(changesetStub.ID))
                //{
                    properties = new Dictionary<string, string>();
                    properties.Add("Comment", changesetStub.Comment);
                    properties.Add("Branch", changesetStub.Branch);
                    properties.Add("Changeset", changesetStub.ID.ToString());
                    properties.Add("CheckinBy", changesetStub.Owner);
                    properties.Add("CheckinDate", changesetStub.CheckinDate.ToLongDateString() + " at " + changesetStub.CheckinDate.ToLongTimeString());

                    string changesetLabel;
                    if (showComments)
                    {
                        changesetLabel = changesetStub.Comment + " (CS" + changesetStub.ID.ToString() + ")";
                    }
                    else
                    {
                        changesetLabel = changesetStub.ID.ToString();
                    }

                    // We display the files associated to changes if requested
                    if (showFiles)
                    {
                        if (groupFiles)
                        {
                            XmlHelper.WriteXMLNode(true, true, xtw, "CS:" + changesetStub.ID.ToString(), changesetLabel, changesetStub.ChangeToFile, new string[] { "Changeset" }, properties);
                        }
                        else
                        {
                            XmlHelper.WriteXMLNode(true, true, xtw, "CS:" + changesetStub.ID.ToString(), changesetLabel, "Changeset", "Expanded", properties);
                        }
                        //Add all of the files related to the changeset
                        for (int i = 0; i < changesetStub.Files.Count; i++)
                        {
                            Dictionary<string, string> props = new Dictionary<string, string>();
                            props.Add("ViewFile", changesetStub.Files[i].FullPath + "&amp;changeset=" + changesetStub.ID);
                            props.Add("Compare", changesetStub.Files[i].FullPath + "&amp;changeset=" + changesetStub.ID);

                            string fileId;
                            string changeType = changesetStub.Files[i].ChangeType;
                            if (IsLinkSelected(changeType))
                            {
                                if (groupFiles)
                                {
                                    fileId = changesetStub.Files[i].FileName;
                                    if (!_allChangeTypes.Contains(changeType))
                                    {
                                        _allChangeTypes.Add(changeType);
                                    }
                                }
                                else
                                {
                                    fileId = changesetStub.Files[i].FileName + ":" + changesetStub.ID.ToString();
                                }
                                XmlHelper.WriteXMLNode(true, true, xtw, fileId, fileId, "File", new string[] { changesetStub.Files[i].ChangeType }, props);
                            }
                        }
                    }
                    else
                    {
                        XmlHelper.WriteXMLNode(true, true, xtw, "CS:" + changesetStub.ID.ToString(), changesetLabel, changesetStub.ChangeToFile, new string[] { "Changeset" }, properties);
                    }

                    // We display the workitems associated to the current changeset if requested
                    if (showWI)
                    {
                        foreach (WorkItem workItem in changesetStub.WorkItems)
                        {
                            properties = new Dictionary<string, string>();
                            properties.Add("WorkItem", workItem.Id.ToString());
                            properties.Add("WorkItemType", workItem.Type.Name);

                            XmlHelper.WriteXMLNode(true, true, xtw, "WI:" + workItem.Id.ToString(), workItem.Title, workItem.Type.Name, new string[] { "WorkItem" }, properties);

                            if (!_allWorkitemTypes.Contains(workItem.Type.Name))
                            {
                                _allWorkitemTypes.Add(workItem.Type.Name);
                            }
                        }
                    }
                //}
                //else
                //{
                //    blackList.Add(changesetStub.ID);
                //}
                // end if ID in interval _searchFirstChangesetId _searchLastChangesetId
            } //end foreach
            if (ShowLabels)
            {
                _allNodeTypes.Add("Label");
                foreach (LabelStub labelStub in _labelStubs.Values)
                {
                    if (labelStub.Changesets.Count > 0)
                    {
                        properties = new Dictionary<string, string>();
                        properties.Add("Comment", labelStub.Comment);
                        properties.Add("Date", labelStub.Date);

                        XmlHelper.WriteXMLNode(true, true, xtw, "LB:" + labelStub.ID.ToString(), labelStub.Name, "Label", "Comment", properties);
                    }
                }
            }

            //Close the Nodes section
            xtw.WriteEndElement();

            #endregion

            #region Write Links
            //Add the links section
            xtw.WriteStartElement("Links");

            List<int> ChangesetAlreadyVisited = CommonUIHelper.GetAllChangesetIdInMerges();

            //Loop through the unique list of work items
            int source = 0;
            int target = 0;
            
            foreach (ChangesetStub changesetStub in _changesetStubs.Values)
            {
                //if (!blackList.Contains(changesetStub.ID))
                //{
                    if (String.IsNullOrEmpty(_groupByFieldReferenceName) == false)
                    {
                        XmlHelper.WriteXMLLink(xtw, changesetStub.GroupyByFieldValue, "CS:" + changesetStub.ID.ToString(), "Contains", string.Empty);
                    }

                    Changeset[] merges = CommonUIHelper.GetMerges(changesetStub.ID);
                    if (merges != null)
                    {
                        for (int i = 0; i < merges.Count() - 1; i++)
                        {
                            source = merges[i].ChangesetId;
                            target = merges[i+1].ChangesetId;

                            if (_changesetStubs.ContainsKey(source) && _changesetStubs.ContainsKey(target))
                            {
                                string changeType = _changesetStubs[target].ChangeToFile;
                                XmlHelper.WriteXMLLink(xtw, "CS:" + source.ToString(), "CS:" + target.ToString(), changeType, string.Empty);
                            }
                        }
                    }
                    if (!ChangesetAlreadyVisited.Contains(changesetStub.ID) || merges != null)
                    {
                        string branchName = changesetStub.Branch;

                        var ChangesetInSameBranch = from k in _changesetStubs.Keys
                                                    orderby k ascending
                                                    where _changesetStubs[k].Branch.Equals(changesetStub.Branch)
                                                    select k;

                        for (int i = 0; i < ChangesetInSameBranch.Count(); i++)
                        {
                            if (i > 0 && ChangesetInSameBranch.ElementAt(i) == changesetStub.ID)
                            {
                                int predecessor = _changesetStubs[ChangesetInSameBranch.ElementAt(i - 1)].ID;
                                //if (!blackList.Contains(predecessor))
                                //{
                                    XmlHelper.WriteXMLLink(xtw, "CS:" + predecessor.ToString(), "CS:" + changesetStub.ID.ToString(), "Predecessor", string.Empty);
                                    break;
                                //}
                            }
                        }
                    }
                //}

                if (showFiles)
                {
                    for (int i = 0; i < changesetStub.Files.Count; i++)
                    {
                        string fileId;
                        if (groupFiles)
                        {
                            //Dans ce cas pas de sous-groupe
                            fileId = changesetStub.Files[i].FileName;
                        }
                        else
                        {
                            fileId = changesetStub.Files[i].FileName + ":" + changesetStub.ID.ToString();
                        }
                        XmlHelper.WriteXMLLink(xtw, "CS:" + changesetStub.ID.ToString(), fileId, "In Changeset/Has Files", string.Empty, "ChangeType", changesetStub.Files[i].ChangeType);
                    }
                }

                if (showWI)
                {
                    foreach (WorkItem workItem in changesetStub.WorkItems)
                    {
                        XmlHelper.WriteXMLLink(xtw, "CS:" + changesetStub.ID.ToString(), "WI:" + workItem.Id.ToString(), "Associated Workitem", string.Empty);
                    }
                }
            }
            if (ShowLabels)
            {
                foreach (LabelStub labelStub in _labelStubs.Values)
                {
                    foreach (int changesetId in labelStub.Changesets)
                    {
                        XmlHelper.WriteXMLLink(xtw, "LB:" + labelStub.ID.ToString(), "CS:" + changesetId.ToString(), "Associated", string.Empty);
                    }
                }
            }

            //Close the links section
            xtw.WriteEndElement();

            #endregion

            #region Write Categories

            xtw.WriteStartElement("Categories");
            for (int i = 0; i < _links.Count; i++)
            {
                if (_links[i].Forward != "Related")
                    XmlHelper.WriteNavigationCategory(xtw, _links[i].ToString(), _links[i].Forward, _links[i].Reverse, "True");
                else
                    XmlHelper.WriteNavigationCategory(xtw, _links[i].ToString(), _links[i].Forward, _links[i].Reverse, "False");
            }
            if (showWI)
            {
                for (int i = 0; i < _allWorkitemTypes.Count; i++)
                {
                    XmlHelper.WriteWorkitemCategory(xtw, _allWorkitemTypes[i]);
                }
            }

            xtw.WriteEndElement();
            #endregion

            #region Write Properties

            //Write a property node and custom properties for hyperlink usage
            string server = UrlHelper.GetBaseMtmUrl(_workItemStore);
            string mtmUrl = server + string.Format("/p:{0}/testing/", project);

            xtw.WriteStartElement("Properties");
            XmlHelper.WriteXMLProperty(xtw, "TestOutcome", "Test Outcome", "System.String");
            if (_useMtmLinks)
                XmlHelper.WriteXMLReference(xtw, "WorkItem", "True", mtmUrl + "/testcase/open?id={0}");
            else
                XmlHelper.WriteXMLReference(xtw, "WorkItem", "True", UrlHelper.GetTWAWorkItemBaseUrl(_workItemStore));

            XmlHelper.WriteXMLReference(xtw, "Changeset", "True", UrlHelper.GetTWAChangesetBaseUrl(_workItemStore));
            XmlHelper.WriteXMLReference(xtw, "ViewFile", "True", UrlHelper.GetTWAChangeBaseUrl(_workItemStore));
            XmlHelper.WriteXMLReference(xtw, "Compare", "True", UrlHelper.GetTWACompareChangeBaseUrl(_workItemStore));
            xtw.WriteEndElement();

            #endregion

            #region Write Styles
            xtw.WriteStartElement("Styles");

            colorConfiguration = new CyclicColorConfiguration("state");
            string label;

            if (showFiles)
            {
                _allNodeTypes.Add("Changeset");
            }
            
            for (int i = 0; i < _allNodeTypes.Count; i++)
            {
                label = _allNodeTypes[i];
                XmlHelper.WriteXMLNodeStyle(xtw, label, colorConfiguration.GetColor(label));
            }

            for (int i = 0; i < _allChangeTypes.Count; i++)
            {
                label = "ChangeType";
                string valueLabel = _allChangeTypes[i];
                string expression = "ChangeType = '" + _allChangeTypes[i] + "'";
                XmlHelper.WriteXMLNodeStyle(xtw, TargetType.Link, label, valueLabel, expression, colorConfiguration.GetColor(label), colorConfiguration.GetColor(label));
            }

            for (int i = 0; i < _allWorkitemTypes.Count; i++)
            {
                label = "WorkItemType";
                string valueLabel = _allWorkitemTypes[i];
                string expression = "WorkItemType = '" + _allWorkitemTypes[i] + "'";
                XmlHelper.WriteXMLNodeStyle(xtw, TargetType.Node, label, valueLabel, expression, colorConfiguration.GetColor(label), colorConfiguration.GetColor(label));
            }

            xtw.WriteEndElement();
            #endregion

            //Close the DGML document
            xtw.WriteEndElement();
            xtw.Close();

            //Open the DGML in Visual Studio
            System.Diagnostics.Process.Start(outputFile);
        }

        /// <summary>
        /// Write the work item and changeset information to a dgml file
        /// </summary>
        /// <param name="outputFile">The file to write the dgml to</param>
        /// <param name="project">The name of the team project</param>
        /// <param name="useMtm">Indicates whether to use MTM hyperlinks (true) or TWA hyperlinks (false)</param>
        private void WriteWorkItemInfo(string outputFile, string project)
        {
            IColorConfiguration colorConfiguration;

            //Create a new DGML document
            XmlTextWriter xtw = XmlHelper.CreateHeader(outputFile);

            #region Write Nodes
            //Add the nodes section
            xtw.WriteStartElement("Nodes");

            //These two arrays are used to store the single category for both files and changesets
            string[] cs = new string[] { "Has Work Items/Has Changesets" };
            string[] file = new string[] { "In Changeset/Has Files", "Associated Work Items/Has Files" };

            // add the group by values if we are grouping by
            if (String.IsNullOrEmpty(_groupByFieldReferenceName) == false)
            {
                for (int i = 0; i < _groupByDistinctValues.Count; i++)
                {
                    XmlHelper.WriteXMLNode(true, true, xtw, _groupByDistinctValues[i], _groupByDistinctValues[i], _groupByFieldName, "Expanded");
                }
            }

            Dictionary<string, string> properties = null;

            //Add the individual work item nodes
            foreach (WorkItemStub workItemStub in _workItemStubs.Values)
            {
                //Stores the unique list of categories associated with this node
                List<string> categories = new List<string>();

                for (int l = 0; l < workItemStub.Related.Count; l++)
                {
                    //Check to see if the category has been added to the array and if not add it
                    if (!categories.Contains(workItemStub.Related[l].Relationship.ToString()))
                        categories.Add(workItemStub.Related[l].Relationship.ToString());
                }

                properties = new Dictionary<string, string>();
                properties.Add("WorkItem", workItemStub.ID.ToString());

                //At this point we have all the categories, write them
                XmlHelper.WriteXMLNode(true, true, xtw, workItemStub.ID.ToString(), workItemStub.Title, workItemStub.WorkItemTypeName, categories.ToArray(), properties);

                if (workItemStub.Changesets != null)
                {
                    //Add the changesets
                    for (int j = 0; j < workItemStub.Changesets.Count; j++)
                    {
                        string csID = "CS" + workItemStub.Changesets[j].ID.ToString(); //Create the changeset ID

                        if (!_dependencyAnalysis)
                        {
                            //Only add the changeset as a group if this is not a dependency analysis graph
                            XmlHelper.WriteXMLNode(true, true, xtw, csID, csID, "Changeset", "Expanded", cs, "Changeset", workItemStub.Changesets[j].ID.ToString());

                            //Add all of the files related to each changeset
                            for (int k = 0; k < workItemStub.Changesets[j].Files.Count; k++)
                            {
                                string fileId = workItemStub.Changesets[j].Files[k].FileName + ";" + csID;
                                XmlHelper.WriteXMLNode(true, true, xtw, workItemStub.Changesets[j].Files[k].FullPath + ";" + csID, fileId, "File");
                            }
                        }
                        else
                        {
                            XmlHelper.WriteXMLNode(true, true, xtw, csID, csID, "Changeset", "Changeset", workItemStub.Changesets[j].ID.ToString());
                        }
                    }
                }

                //add the test plan and test results nodes
                if (_testPlans != null)
                {
                    //Add the test plan
                    foreach (TestPlanInfo testPlanInfo in _testPlans.Values)
                    {
                        //The ID for the purposes of the DGML is "TP" + the test plan ID
                        XmlHelper.WriteXMLNode(true, true, xtw, "TP" + testPlanInfo.TestPlanID,
                            "Test Plan: " + testPlanInfo.TestPlanName, "Test Plan", "Collapsed");

                        //Add the test runs
                        for (int k = 0; k < testPlanInfo.TestRuns.Count; k++)
                        {
                            XmlHelper.WriteXMLNode(true, true, xtw, "TestRun" + testPlanInfo.TestRuns[k].ID.ToString(),
                                "Test Run " + testPlanInfo.TestRuns[k].ID.ToString(), "Test Run", "Expanded");
                        }
                    }

                    //Add the test results
                    for (int j = 0; j < workItemStub.TestResults.Count; j++)
                    {
                        //The ID for the purposes of the DGML is "TR" + the test result ID
                        string outcome = string.Format("{0}\n{1}", "Test Case: " + workItemStub.TestResults[j].TestCaseID, Enum.GetName(typeof(TestOutcome), workItemStub.TestResults[j].Outcome));
                        string trID = "TR" + workItemStub.TestResults[j].TestRunID.ToString() + workItemStub.TestResults[j].TestResultID.ToString() + workItemStub.TestResults[j].TestCaseID.ToString();
                        XmlHelper.WriteXMLNode(true, true, xtw, trID, outcome, "Test Result", "TestOutcome", Enum.GetName(typeof(TestOutcome), workItemStub.TestResults[j].Outcome));
                    }
                }
            }

            //We only add a discrete list of files if we are running a dependency analysis - otherwise all files
            //are added as part of their changesets above and are broken out into distinct files
            if (_dependencyAnalysis)
            {
                foreach (string fileName in _allFiles.Keys)
                {
                    XmlHelper.WriteXMLNode(true, true, xtw, fileName, fileName, "File", file);
                }
            }

            //Close the Nodes section
            xtw.WriteEndElement();

            #endregion

            #region Write Links
            //Add the links section
            xtw.WriteStartElement("Links");

            //Loop through the unique list of work items
            foreach (WorkItemStub workItemStub in _workItemStubs.Values)
            {
                if (String.IsNullOrEmpty(_groupByFieldReferenceName) == false)
                {
                    XmlHelper.WriteXMLLink(xtw, workItemStub.GroupyByFieldValue, workItemStub.ID.ToString(), "Contains", string.Empty);
                }

                //Add each related item to the DGML
                for (int j = 0; j < workItemStub.Related.Count; j++)
                {
                    String label = (workItemStub.Related[j].IsDeleted ? "<<Deleted>>" : string.Empty);

                    XmlHelper.WriteXMLLink(xtw, workItemStub.ID.ToString(), workItemStub.Related[j].ID.ToString(), workItemStub.Related[j].Relationship.ToString(), label);

                    if (String.IsNullOrEmpty(_groupByFieldReferenceName) == false)
                    {
                        XmlHelper.WriteXMLLink(xtw, workItemStub.GroupyByFieldValue, workItemStub.Related[j].ID.ToString(), "Contains", label);
                    }
                }

                //Add the changesets and files to the DGML
                if (workItemStub.Changesets != null)
                {
                    for (int j = 0; j < workItemStub.Changesets.Count; j++)
                    {
                        //for each changeset, get the ID and add a link from the work item to the changeset
                        string csID = "CS" + workItemStub.Changesets[j].ID.ToString();
                        XmlHelper.WriteXMLLink(xtw, workItemStub.ID.ToString(), csID, "Has Work Items/Has Changesets", string.Empty);

                        for (int k = 0; k < workItemStub.Changesets[j].Files.Count; k++)
                        {
                            if (!_dependencyAnalysis)
                            {
                                //Add a link from the changeset to the file
                                XmlHelper.WriteXMLLink(xtw, csID, workItemStub.Changesets[j].Files[k].FullPath + ";" + csID, "In Changeset/Has Files", string.Empty);
                            }
                            else
                            {
                                XmlHelper.WriteXMLLink(xtw, workItemStub.ID.ToString(), workItemStub.Changesets[j].Files[k].FileName, "Associated Work Items/Has Files", string.Empty);
                                XmlHelper.WriteXMLLink(xtw, csID, workItemStub.Changesets[j].Files[k].FileName, "In Changeset/Has Files", string.Empty);
                            }
                        }   //End of file loop
                    }   //End of changeset loop
                }   //End of if statement

                //Add the test plans
                foreach (TestPlanInfo tesPlan in _testPlans.Values)
                {
                    for (int n = 0; n < tesPlan.TestRuns.Count; n++)
                    {
                        XmlHelper.WriteXMLLink(xtw, "TP" + tesPlan.TestPlanID.ToString(), "TestRun" + tesPlan.TestRuns[n].ID.ToString(), "Contains", string.Empty);
                    }
                }

                //Add the test results
                if (workItemStub.TestResults != null)
                {
                    for (int j = 0; j < workItemStub.TestResults.Count; j++)
                    {
                        string trID = "TR" + workItemStub.TestResults[j].TestRunID.ToString() + workItemStub.TestResults[j].TestResultID.ToString() + workItemStub.TestResults[j].TestCaseID.ToString();
                        XmlHelper.WriteXMLLink(xtw, workItemStub.TestResults[j].TestCaseID.ToString(), trID);
                        XmlHelper.WriteXMLLink(xtw, "TestRun" + workItemStub.TestResults[j].TestRunID.ToString(), trID, "Contains", string.Empty);
                    }
                }
            }

            //Close the links section
            xtw.WriteEndElement();

            #endregion

            #region Write Categories

            xtw.WriteStartElement("Categories");
            for (int i = 0; i < _links.Count; i++)
            {
                if (_links[i].Forward != "Related")
                    XmlHelper.WriteNavigationCategory(xtw, _links[i].ToString(), _links[i].Forward, _links[i].Reverse, "True");
                else
                    XmlHelper.WriteNavigationCategory(xtw, _links[i].ToString(), _links[i].Forward, _links[i].Reverse, "False");
            }
            xtw.WriteEndElement();
            #endregion

            #region Write Properties

            //Write a property node and custom properties for hyperlink usage
            string server = UrlHelper.GetBaseMtmUrl(_workItemStore);
            string mtmUrl = server + string.Format("/p:{0}/testing/", project);

            xtw.WriteStartElement("Properties");
            XmlHelper.WriteXMLProperty(xtw, "TestOutcome", "Test Outcome", "System.String");
            if (_useMtmLinks)
                XmlHelper.WriteXMLReference(xtw, "WorkItem", "True", mtmUrl + "/testcase/open?id={0}");
            else
                XmlHelper.WriteXMLReference(xtw, "WorkItem", "True", UrlHelper.GetTWAWorkItemBaseUrl(_workItemStore));

            XmlHelper.WriteXMLReference(xtw, "Changeset", "True", UrlHelper.GetTWAChangesetBaseUrl(_workItemStore));
            xtw.WriteEndElement();

            #endregion

            #region Write Styles
            xtw.WriteStartElement("Styles");

            colorConfiguration = new CyclicColorConfiguration("state");

            for (int i = 0; i < _allNodeTypes.Count; i++)
            {
                string label;

                label = _allNodeTypes[i];

                XmlHelper.WriteXMLNodeStyle(xtw, label, colorConfiguration.GetColor(label));
            }

            xtw.WriteEndElement();
            #endregion

            //Close the DGML document
            xtw.WriteEndElement();
            xtw.Close();

            //Open the DGML in Visual Studio
            System.Diagnostics.Process.Start(outputFile);
        }

        /// <summary>
        /// Loads the link types for processing
        /// </summary>
        private List<TempLinkType2> LoadLinkTypes(string project, bool AddChangeTypes)
        {
            List<TempLinkType2> links = new List<TempLinkType2>();

            WorkItemLinkTypeCollection linkTypes = _workItemStore.Projects[project].Store.WorkItemLinkTypes;

            foreach (WorkItemLinkType t in linkTypes)
            {
                links.Add(new TempLinkType2() { Reverse = t.ForwardEnd.Name, Forward = t.ReverseEnd.Name });
            }

            links.Add(new TempLinkType2() { Forward = "Has Work Items", Reverse = "Has Changesets" });
            links.Add(new TempLinkType2() { Forward = "Test Results", Reverse = "Test Results" });
            links.Add(new TempLinkType2() { Forward = "In Changeset", Reverse = "Has Files" });

            //Used for dependency analysis only
            links.Add(new TempLinkType2() { Forward = "Associated Work Items", Reverse = "Has Files" });

            if (AddChangeTypes)
            {
                foreach (Microsoft.TeamFoundation.VersionControl.Client.ChangeType changeType in Enum.GetValues(typeof(Microsoft.TeamFoundation.VersionControl.Client.ChangeType)))
                {
                    links.Add(new TempLinkType2() { Forward = changeType.ToString(), Reverse = changeType.ToString() });
                }
            }

            return links;
        }

        /// <summary>
        /// Checks to see if the work item stub exists
        /// </summary>
        /// <param name="id">The ID of the work item to find</param>
        /// <returns>The work item stub or null if it isn't found</returns>
        private WorkItemStub GetStub(int id)
        {
            WorkItemStub s;
            _workItemStubs.TryGetValue(id, out s);

            return s;
        }

        /// <summary>
        /// Gets the stub associated with the work item if it exists, otherwise a new one is
        /// created, added to the collection and returned
        /// </summary>
        /// <param name="wi">The Work Item to check</param>
        /// <returns>The associated WorkItemStub</returns>
        private WorkItemStub CheckStub(WorkItem wi)
        {
            //Perform a find on the ID, if it is found, s is populated
            WorkItemStub s = GetStub(wi.Id);

            //If it isn't found, create a new work item stub
            if (s == null)
            {
                s = new WorkItemStub();
                s.ID = wi.Id;
                s.Title = wi.Fields[CoreField.Title].Value.ToString();
                s.WorkItemTypeName = wi.Fields[CoreField.WorkItemType].Value.ToString();

                //if (!_allNodeTypes.Contains(s.WorkItemTypeName.ToLower()))
                //    _allNodeTypes.Add(s.WorkItemTypeName.ToLower());

                if (!_allNodeTypes.Contains(s.WorkItemTypeName))
                    _allNodeTypes.Add(s.WorkItemTypeName);


                // if we are grouping by, then add the distinct value if it's not there already
                if (String.IsNullOrEmpty(_groupByFieldReferenceName) == false)
                {
                    s.GroupyByFieldValue = wi.Fields[_groupByFieldReferenceName].Value.ToString();

                    if (!_groupByDistinctValues.Contains(s.GroupyByFieldValue))
                        _groupByDistinctValues.Add(s.GroupyByFieldValue);
                }


                _workItemStubs.Add(s.ID, s);
            }

            return s;
        }

        /// <summary>
        /// The work item to process
        /// </summary>
        /// <param name="wi"></param>
        private void ProcessWorkItemCS(WorkItem wi)
        {
            //Get a reference to or add the wi to the workitemstub list
            WorkItemStub s = CheckStub(wi);

            // ajouter option "display removed links"
            // on fait le test ici
            // le lien doit être affiché pour gérer une catégorie à part.

            //Loop through the work items linked to this work item
            for (int i = 0; i < wi.WorkItemLinks.Count; i++)
            {
                if (_full || IsLinkSelected(wi.WorkItemLinks[i].LinkTypeEnd.Name))
                {
                    ProcessLinks(wi, wi.WorkItemLinks[i], s);
                }
            }

            if (_full || IsLinkSelected("Changesets"))
            {
                GetChangesets(wi, s);
            }

            if (s.WorkItemTypeName == "Test Case")
            {
                if (_full || IsLinkSelected("Test Results"))
                {
                    GetTestResults(s, wi.Project.Name);
                }
            }
        }

        /// <summary>
        /// Processes a relationship between two work items
        /// </summary>
        /// <param name="wi">The source work item</param>
        /// <param name="wiLink">The link to the target work item</param>
        /// <param name="s">The work item stub that corrosponds to the source work item</param>
        private void ProcessLinks(WorkItem wi, WorkItemLink wiLink, WorkItemStub s)
        {
            int targetID = wiLink.TargetId;
            bool isDeleted = (wiLink.RemovedDate.Year == 9999) ? false : true;

            if (!isDeleted || _showDeletedLinks)
            {
                //Check to see if the work item that is related to this one is in the list of work items and if not, add it by
                //calling this method recursively. If it is in the list of work items then we've already
                //seen this work item and we just have to figure out how it is linked to this work item
                WorkItemStub result;

                _workItemStubs.TryGetValue(targetID, out result);

                //If the work item hasn't been processed yet, process it
                if (result == null)
                    ProcessWorkItemCS(_workItemStore.GetWorkItem(targetID));

                //Check to see if the ID and relationship match (we can have more than one relationship between two work items
                //for example, we could have a parent/child relationship and also a predecessor/successor relationship
                string findId;
                findId = targetID.ToString() + wiLink.LinkTypeEnd.Name;
                WorkItemRelationship result1 = s.Related.Find(stub => { return stub.ToString() == findId; });

                if (result1 == null)
                {
                    WorkItemStub workItemStub;
                    bool found = false;
                    //Before we add this relationship in, make sure we have never added this relationship anywhere at all
                    //so we make sure each relationship (forward/reverse end of the same relationship) is unique in the
                    //entire result set.                

                    if (_workItemStubs.TryGetValue(targetID, out workItemStub))
                    {
                        for (int k = 0; k < workItemStub.Related.Count; k++)
                        {
                            if (workItemStub.Related[k].ID == wi.Id)
                            {
                                found = true;
                                break;
                            }
                        }
                    }

                    //If we didn't find an existing relationship, make sure we add it to the 
                    //work item at the reverse end of the relationship. For example, in a Parent/
                    //Child relationship the Child is the Forward end of the relationship and
                    //Parent is the reverse end of the relationship. We need to add this relationship
                    //to the Parent to say that the parent has children so when the links are
                    //written out to the DGML they point the right direction
                    if (!found)
                    {
                        //get the relationship
                        TempLinkType2 t = GetRelationship(wiLink.LinkTypeEnd.Name);
                        //Determine if this is the forward or reverse end of the relationship
                        if (t.Reverse == wiLink.LinkTypeEnd.Name)
                        {
                            //This is the reverse end of the relationship so add it, otherwise skip it altogether
                            s.Related.Add(new WorkItemRelationship() { ID = targetID, IsDeleted = isDeleted, End = wiLink.LinkTypeEnd.Name, Relationship = GetRelationship(wiLink.LinkTypeEnd.Name) });
                        }
                    }
                }
            }
        }

        #endregion

        /// <summary>
        /// This method takes a work item and examines it's relationships to determine what
        /// the root work item is. The use of this method is in dependency analysis which
        /// takes the following ReqA --> TaskB --> ChangesetC and changes it to 
        /// ReqA --> Changeset C. This base work item has changesets otherwise we would
        /// not be processing it. For our purposes we would be starting with TaskB and
        /// looking for ReqA or some other parent
        /// </summary>
        /// <param name="wi">The work item to discover the parent of</param>
        private void GetWorkItemParent(WorkItemStub stub)
        {
            //The rules for discovery are as follows:
            //1. Walk the parent/child relationship from the reverse to the forward node
            //2. Check the related work item ONLY when going from a Bug to a Test Case
            //   - never the reverse or you get trapped in a recursive loop
            //3. Check the affected by/affects from the reverse to the forward (in other
            //   words the standard relationship is a Requiremenet is Affected By a
            //   Change Request which has an associated changeset or child task

            WorkItemStub s = null;  //stores the related work item stubs
            bool found = false;     //indicates if we found a valid link to persue

            //Because of how the relationships are constructed, only one end of the relationship has
            //the actual link to the relationship. For example, Story1 has relationships with Task1
            //and Task2 but Task1 and Task2 don't know anything about Story1. So, when we are going
            //through this list, we have to determine which other work items are related to the work
            //item we're looking at right now.

            //NOTE I may have to come back to this because it may be a fallacy that just because
            //story1 is related to task1 and task2 doesn't mean that there isn't a "story3" that
            //is the parent of Story1
            //This returns a list of parent work items that are related to thsi work item.
            List<WorkItemStub> r = GetRelatedWorkItems(stub);
            for (int i = 0; i < r.Count; i++)
            {
                s = GetStub(r[i].ID);
                GetWorkItemParent(s);
                found = true;
            }

            //If we haven't found any other links that are parent/child then this
            //must be the root that we are associating the changesets with
            if (!found)
            {
                if (_tempStub != stub)
                {
                    //Associate the changeset with this work item
                    for (int k = 0; k < _tempStub.Changesets.Count; k++)
                    {
                        stub.Changesets.Add(_tempStub.Changesets[k]);
                    }
                }
            }
        }

        /// <summary>
        /// Gets all work item stubs related to a given work item stub
        /// </summary>
        /// <param name="stub">The work item to get relationships for</param>
        /// <returns>A list of related work item stubs</returns>
        private List<WorkItemStub> GetRelatedWorkItems(WorkItemStub stub)
        {
            List<WorkItemStub> related = new List<WorkItemStub>();

            for (int i = 0; i < _workItemStubs.Count; i++)
            {
                for (int k = 0; k < _workItemStubs[i].Related.Count; k++)
                {
                    if ((_workItemStubs[i].Related[k].ID == stub.ID) && (_workItemStubs[i].Related[k].End == "Child"))
                    {
                        related.Add(_workItemStubs[i]);
                    }
                }
            }
            return related;
        }

        /// <summary>
        /// Determines if the link for processing is selected
        /// </summary>
        /// <param name="link">The link end name</param>
        /// <returns>True if the link should be processed, otherwise false</returns>
        private bool IsLinkSelected(string link)
        {
            if (link.Contains(","))
            {
                string[] links = link.Split(new char[] { ',' });
                foreach (string lk in links)
                {
                    if (!GetLinkType(lk.Trim()).Selected)
                    {
                        return false;
                    }
                }
                return true;
            }
            else
            {
                return GetLinkType(link).Selected;
            }
        }

        /// <summary>
        /// Gets a link type from the list of link types
        /// </summary>
        /// <param name="link">The name of the link end</param>
        /// <returns>The link type</returns>
        private TempLinkType GetLinkType(string link)
        {
            return _selectedLinks[link];
        }

        /// <summary>
        /// Retrieves changesets associated with a work item
        /// 
        /// This call only needs to be executed once per work item id.
        /// 
        /// If multiple calls are made we ignore all calls after the first one
        /// </summary>
        /// <param name="wi">The work item to retrieve changesets for</param>
        /// <param name="stub">The work item stub to add them to</param>
        private void GetChangesets(WorkItem wi, WorkItemStub stub)
        {
            if (_visitedChangeSetsForWorkItem.ContainsKey(wi.Id)) return;

            _visitedChangeSetsForWorkItem.Add(wi.Id, null);

            //Loop through all of the links in the work item. Note that this is the links
            //not the work item links so this includes things like hyperlinks, versioned items
            //and changeset links
            for (int i = 0; i < wi.Links.Count; i++)
            {
                //Determine if this is an external link which is an indication of a changeset link
                if (wi.Links[i].BaseType == BaseLinkType.ExternalLink)
                {
                    int cs_id;
                    ExternalLink ex = (ExternalLink)wi.Links[i];

                    //We have a valid changeset link type
                    if (ex.ArtifactLinkType.Equals(_workItemStore.RegisteredLinkTypes[ArtifactLinkIds.Changeset]))
                    {
                        //Get the ID of the changeset
                        ArtifactId artifact = LinkingUtilities.DecodeUri(ex.LinkedArtifactUri);
                        cs_id = Convert.ToInt32(artifact.ToolSpecificId);

                        //It is a changeset, validate that we haven't processed it already
                        ChangesetStub c = stub.Changesets.Find(
                               delegate(ChangesetStub s)
                               {
                                   return s.ID == cs_id;
                               }
                           );

                        //It wasn't found, process it
                        if (c == null)
                        {
                            //It is a changeset so get the specific changeset
                            Changeset cs = _vcs.GetChangeset(cs_id);
                            ChangesetStub csStub = new ChangesetStub();
                            csStub.ID = cs_id;

                            //Loop through the files in the changeset which is represented by the Changes
                            for (int j = 0; j < cs.Changes.Count(); j++)
                            {
                                //Add the files to the changeset stub
                                FileChangeInfo fc = new FileChangeInfo();
                                fc.ChangeType = cs.Changes[j].ChangeType.ToString();
                                fc.FullPath = cs.Changes[j].Item.ServerItem;
                                csStub.Files.Add(fc);

                                //Check to see if we have added this file to the master list,
                                //if we haven't, add it
                                if (_allFiles.ContainsKey(fc.FileName) == false)
                                    _allFiles.Add(fc.FileName, fc);
                            }
                            stub.Changesets.Add(csStub);

                            //Loop through the files in the changeset which is represented by the Changes
                            for (int j = 0; j < cs.Changes.Count(); j++)
                            {
                                ChangesetFilesToWorkItems(cs.Changes[j].Item.ServerItem);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets the entire history of a single file.
        /// 
        /// This code only needs to run once per file (since it analyses it's entire history).
        /// 
        /// Therefore if we have been already called once for a given filepath we simple return avoiding
        /// duplicate processing
        /// </summary>
        /// <param name="serverpath">The path to the item on the server who's history to retrieve</param>
        private void ChangesetFilesToWorkItems(string serverpath)
        {
            if (_visitedServerPath.ContainsKey(serverpath)) return;

            _visitedServerPath.Add(serverpath, null);

            //This syntax retrieves the entire history of a file - but not across branches
            //as that typically won't have as big an impact on changes
            System.Collections.IEnumerable history = _vcs.QueryHistory(serverpath, VersionSpec.Latest,
                0, RecursionType.OneLevel, null, null, null, Int32.MaxValue, true, false);

            //At this point history contains every changeset the given file has been involved in
            //Next we need to get all of the work items associated with that changeset and hence that file
            //and add all items to the drawing surface (eventually).
            foreach (Changeset cs in history)
            {
                WorkItem[] wi = cs.WorkItems;
                for (int i = 0; i < wi.Length; i++)
                {
                    ProcessWorkItemCS(wi[i]);
                }
            }
        }

        /// <summary>
        /// Get the results associated with a test case
        /// </summary>
        /// <param name="stub">The work item to get the results for</param>
        private void GetTestResults(WorkItemStub stub, string project)
        {
            //Return all of the test results in which this test case was executed
            string query = string.Format("SELECT * FROM TestResult WHERE TestCaseId = {0}", stub.ID);
            int planId = 0;

            //Get the list of results for the test case
            foreach (ITestCaseResult tcr in _testManagementTeamProject.TestResults.Query(query))
            {
                TestPlanInfo plan;

                #region Get the Test Plan
                ITestRun tr = tcr.GetTestRun();

                planId = tr.TestPlanId;














                //Is this part of a test plan that we already have? If not, add it
                if (_testPlans.TryGetValue(planId, out plan) == false)
                {
                    string planQuery = string.Format("SELECT * FROM TestPlan WHERE PlanId = {0}", planId);
                    foreach (ITestPlan testPlan in _testManagementTeamProject.TestPlans.Query(planQuery))
                    {
                        plan = new TestPlanInfo() { TestPlanID = planId, TestPlanName = testPlan.Name };
                        _testPlans.Add(planId, plan);
                        break;
                    }
                }
                #endregion

                //Check to see if we've added the test run already
                bool found = false;
                for (int k = 0; k < plan.TestRuns.Count; k++)
                {
                    if (plan.TestRuns[k].ID == tcr.TestRunId)
                    {
                        found = true;
                    }
                }

                //If not, add it
                if (!found)
                    plan.TestRuns.Add(new TestRunStub() { ID = tcr.TestRunId, Url = UrlHelper.GetMtmTestRunUrl(_workItemStore, tcr.TestRunId, project) });

                //add the results
                TestResultStub resultStub = new TestResultStub();
                resultStub.TestPlan = plan;
                resultStub.TestCaseID = tcr.TestCaseId;
                resultStub.TestResultID = tcr.TestResultId;
                resultStub.TestRunID = tcr.TestRunId;
                resultStub.Outcome = tcr.Outcome;
                resultStub.Url = UrlHelper.GetMtmTestResultsUrl(_workItemStore, tcr.TestRunId, tcr.TestResultId, project);
                stub.TestResults.Add(resultStub);
            }
        }

        #region code from TfsBranchHistory Codeplex project
        // http://tfsbranchhistory.codeplex.com/SourceControl/changeset/changes/33110

        private void QueryLabels(string serverItem, List<BaseListItem> historList)
        {
            VersionControlServer vcs = CommonUIHelper.Helper._tfs.GetService<VersionControlServer>();
            VersionControlLabel[] labels = null;

            // The first three arguments here are null because we do not
            // want to filter by label name, scope, or owner.
            // Since we don't need the server to send back the items in
            // the label, we get much better performance by ommitting
            // those through setting the fourth parameter to false.
            labels = vcs.QueryLabels(null, serverItem, null, false, serverItem, VersionSpec.Latest);

            //string str = "Labels" + Environment.NewLine;
            foreach (VersionControlLabel label in labels)
            {
                LabelItem labelItem = new LabelItem(label);
                historList.Add(labelItem);
                // Display the label's name and when it was last modified.
                //str += label.Name + label.LastModifiedDate.ToString("g") + Environment.NewLine;
            }
        }

        private int GetRootChangesetId(ref string serverItem, VersionControlServer vcs)
        {
            // local variables
            string branchFrom_ServerItem = serverItem;
            VersionSpec branchFrom_ChangesetVersionSpec = VersionSpec.Latest;
            int branchFrom_DeletionId = 0;
            int branchFrom_ChangesetId = 0;
            Change branchChange;

            // browse history
            do
            {
                IEnumerable changesets = vcs.QueryHistory(branchFrom_ServerItem,
                                                                branchFrom_ChangesetVersionSpec,
                                                                branchFrom_DeletionId,
                                                                RecursionType.None,
                                                                null,
                                                                null,
                                                                null,
                                                                Int32.MaxValue,
                                                                true,
                                                                false,
                                                                false);

                branchChange = null;

                foreach (Changeset changeset in changesets)
                {
                    foreach (Change change in changeset.Changes)
                    {
                        if ((change.ChangeType & ChangeType.Branch) != ((ChangeType)0))
                        {
                            branchChange = change;
                            Item branchChangeItem = change.Item;

                            ItemSpec itemSpec = new ItemSpec(branchChangeItem.ServerItem, RecursionType.None, branchChangeItem.DeletionId);
                            ItemSpec[] itemSpecArray = new ItemSpec[1] { itemSpec };
                            ChangesetVersionSpec changesetVersionSpec = new ChangesetVersionSpec(branchChangeItem.ChangesetId);

                            BranchHistoryTreeItem[][] branchHistoryTreeItemArrayOfArrays = vcs.GetBranchHistory(itemSpecArray, changesetVersionSpec);
                            BranchHistoryTreeItem branchHistoryTreeItem = branchHistoryTreeItemArrayOfArrays[0][0].GetRequestedItem();

                            Item branchFromItem = branchHistoryTreeItem.Relative.BranchFromItem;

                            if (branchFromItem != null)
                            {
                                branchFrom_ServerItem = branchFromItem.ServerItem;
                                branchFrom_DeletionId = branchFromItem.DeletionId;
                                branchFrom_ChangesetId = branchFromItem.ChangesetId;
                                branchFrom_ChangesetVersionSpec = new ChangesetVersionSpec(branchFromItem.ChangesetId);
                            }
                        }
                        else if ((change.ChangeType & ChangeType.Add) != ((ChangeType)0))
                        {
                            branchChange = null;
                            Item branchChangeItem = change.Item;

                            branchFrom_ServerItem = change.Item.ServerItem;
                            branchFrom_ChangesetId = change.Item.ChangesetId;
                        }
                    }
                }

            } while (branchChange != null);

            serverItem = branchFrom_ServerItem;
            return branchFrom_ChangesetId;
        }

        private void GetBranchHistory(string serverItem, int changesetId, int deletionId, System.Collections.Generic.List<BaseListItem> historList)
        {
            VersionControlServer vcs = CommonUIHelper.Helper._tfs.GetService<VersionControlServer>();

            // browse history
            QueryHistory(serverItem, changesetId, deletionId, historList);

            // follow branches
            ItemSpec itemSpec = new ItemSpec(serverItem, RecursionType.None, deletionId);
            BranchHistoryTreeItem[][] branchHistoryTreeItemArrayOfArrays = vcs.GetBranchHistory(new ItemSpec[] { itemSpec }, new ChangesetVersionSpec(changesetId));
            foreach (BranchHistoryTreeItem branchHistoryTreeItem in branchHistoryTreeItemArrayOfArrays[0][0].Children)
                GetBranchHistory(branchHistoryTreeItem, historList);

            //if (String.IsNullOrEmpty(_groupByFieldReferenceName) == false && _groupByFieldReferenceName.Equals("Branch"))
            //{
            //    _groupByDistinctValues = CommonUIHelper.GetRegisteredBranches();
            //}
        }

        private void GetBranchHistory(BranchHistoryTreeItem historyTreeItem, System.Collections.Generic.List<BaseListItem> historList)
        {
            // check whether we have sufficient information to display the target branch
            if (historyTreeItem.Relative.BranchToItem == null)
                return;

            // browse history
            QueryHistory(historyTreeItem.Relative.BranchToItem.ServerItem
                , historyTreeItem.Relative.BranchToItem.ChangesetId
                , historyTreeItem.Relative.BranchToItem.DeletionId
                , historList);

            // recurse into children
            foreach (BranchHistoryTreeItem branchHistoryTreeItem in historyTreeItem.Children)
                GetBranchHistory(branchHistoryTreeItem, historList);
        }

        private void QueryHistory(string serverItem, int changesetId, int deletionId, System.Collections.Generic.List<BaseListItem> historList)
        {
            if (CommonUIHelper.IsItemInASelectedBranch(serverItem))
            {
                VersionControlServer vcs = CommonUIHelper.Helper._tfs.GetService<VersionControlServer>();

                // local variables
                VersionSpec branchFrom_ChangesetVersionSpec = (changesetId == 0 ? VersionSpec.Latest : new ChangesetVersionSpec(changesetId));
                string branchFrom_ServerItem = serverItem;
                int branchFrom_DeletionId = deletionId;

                DateVersionSpec versionFrom = (_searchFirstDate == DateTime.MinValue ? null : new DateVersionSpec(_searchFirstDate));
                DateVersionSpec versionTo = (_searchLastDate == DateTime.MinValue ? null : new DateVersionSpec(_searchLastDate));
                int firstChangesetId = _searchFirstChangesetId;
                int lastChangesetId = (_searchLastChangesetId == 0 ? int.MaxValue : _searchLastChangesetId);

                // browse history
                List<Changeset> changesets = new List<Changeset>(vcs.QueryHistory(branchFrom_ServerItem,
                                                branchFrom_ChangesetVersionSpec,
                                                branchFrom_DeletionId,
                                                RecursionType.None,
                                                null,
                                                versionFrom,
                                                versionTo,
                                                Int32.MaxValue,
                                                true,
                                                false,
                                                false).Cast<Changeset>());
                changesets.FindAll(changeset => changeset.ChangesetId >= firstChangesetId && changeset.ChangesetId <= lastChangesetId)
                    .ForEach(changeset => new List<Change>(changeset.Changes).ForEach(change => historList.Add(new HistoryItem(changeset, change))));

                changesets = new List<Changeset>(
                                    from changeset in changesets
                                    //where changeset.ChangesetId >= firstChangesetId && changeset.ChangesetId <= lastChangesetId
                                    from change in changeset.Changes
                                    where change.Item.ServerItem.Equals(serverItem)
                                            && (change.ChangeType & ChangeType.Branch) != ((ChangeType)0)
                                            || (change.ChangeType & ChangeType.Merge) != ((ChangeType)0)
                                    select changeset);

                changesets.ForEach(changeset => new List<Change>(changeset.Changes).ForEach(delegate(Change change)
                    {

                        List<string> branches = CommonUIHelper.GetRegisteredBranchItemsFromBranch(change.Item.ServerItem);

                        foreach (string branchPath in branches)
                        {
                            int Id = change.Item.ChangesetId;
                            string sourcePath = branchPath;
                            ChangesetVersionSpec sourceVersion = new ChangesetVersionSpec(Id);
                            int sourceDeletionId = 0;

                            string targetPath = change.Item.ServerItem;
                            ChangesetVersionSpec targetVersion = new ChangesetVersionSpec(Id);
                            int targetDeletionId = 0;

                            try
                            {
                                ChangesetMergeDetails details = vcs.QueryMergesWithDetails(sourcePath,
                                                                sourceVersion,
                                                                sourceDeletionId,
                                                                targetPath,
                                                                targetVersion,
                                                                targetDeletionId,
                                                                null,
                                                                null,
                                                                RecursionType.None);

                                if (details.Changesets.Count() > 0)
                                {
                                    string sourceBranchPath = CommonUIHelper.ConvertBranch(sourcePath);
                                    string targetBranchPath = CommonUIHelper.ConvertBranch(targetPath);

                                    CommonUIHelper.RegisterMergedChangesets(Id, sourceBranchPath, targetBranchPath, details.Changesets);
                                }
                            }
                            // An Exception is thrown when there is no relation between source and target branches                            
                            catch (ItemNotFoundException) { }
                        }
                    }));

                List<VersionControlLabel> labels = new List<VersionControlLabel>(vcs.QueryLabels(null, serverItem, null, false, serverItem, VersionSpec.Latest));
                labels.FindAll(label => label.Name.Contains(_labelPattern)).ForEach(label => historList.Add(new LabelItem(label)));
            }
        }
        

        private void AppendChangesetStubs(ChangesetStub stub)
        {
            if (!_changesetStubs.Keys.Contains(stub.ID))
            {
                _changesetStubs.Add(stub.ID, stub);
            }
            else
            {
                ChangesetStub oldStub = _changesetStubs[stub.ID];
                if (stub.Files.Count > 0 && !oldStub.Files.Contains(stub.Files[0]))
                {
                    _changesetStubs.Remove(stub.ID);
                    oldStub.Files.Add(stub.Files[0]);
                    _changesetStubs.Add(oldStub.ID, oldStub);
                }
            }
        }

        private void AppendLabelStubs(LabelStub stub)
        {
            if (!_labelStubs.Keys.Contains(stub.ID))
            {
                _labelStubs.Add(stub.ID, stub);
            }
            else
            {
                LabelStub oldStub = _labelStubs[stub.ID];
                _labelStubs.Remove(stub.ID);

                foreach (int changesetId in stub.Changesets)
                {
                    if (!oldStub.Changesets.Contains(changesetId))
                    {
                        oldStub.Changesets.Add(changesetId);
                    }
                }
                _labelStubs.Add(oldStub.ID, oldStub);
            }
        }

        public void ProcessCSHistory(System.Collections.Generic.List<BaseListItem> historList)
        {
            System.Collections.Generic.List<BaseListItem> historList_ = historList;

            foreach (BaseListItem item in historList_)
            {
                HistoryItem historyItem = item as HistoryItem;
                if (historyItem != null)
                {
                    ChangesetStub stub = new ChangesetStub();

                    // Changeset ID
                    stub.ID = historyItem.Changeset.ChangesetId;

                    // Changeset COMMENT
                    string comment = historyItem.Changeset.Comment;
                    comment.Replace(System.Environment.NewLine, " ");
                    stub.Comment = comment;

                    // Changeset OWNER
                    string username = historyItem.Changeset.Committer;
                    if (username.IndexOf(@"\") != -1)
                        username = username.Substring(username.IndexOf(@"\") + 1);
                    stub.Owner = username;

                    // Changeset CHECKINDATE
                    stub.CheckinDate = historyItem.Changeset.CreationDate;

                    // Changeset CHANGETOFILE
                    stub.ChangeToFile = PendingChange.GetLocalizedStringForChangeType(historyItem.Change.ChangeType);

                    // Changeset FILES
                    FileChangeInfo file = new FileChangeInfo();
                    file.FullPath = historyItem.Change.Item.ServerItem;
                    file.ChangeType = historyItem.Change.ChangeType.ToString();
                    List<FileChangeInfo> files = stub.Files;
                    files.Add(file);
                    stub.Files = files;

                    // Changeset WORKITEMS
                    // OPTI: Ligne couteuse, ajouter test affichage des WI?
                    stub.WorkItems = historyItem.Changeset.WorkItems;

                    // Changeset BRANCH
                    if (String.IsNullOrEmpty(_groupByFieldReferenceName) == false && _groupByFieldReferenceName.Equals("Branch"))
                    {
                        stub.Branch = CommonUIHelper.ConvertBranch(historyItem.Change.Item.ServerItem);

                        if (String.IsNullOrEmpty(_groupByFieldReferenceName) == false && _groupByFieldReferenceName.Equals("Branch"))
                        {
                            if (!_groupByDistinctValues.Contains(stub.Branch))
                                _groupByDistinctValues.Add(stub.Branch);
                        }
                    }

                    // TODO for the moment we only support GroupBy Branch
                    stub.GroupyByFieldValue = stub.Branch;

                    AppendChangesetStubs(stub);
                }

                LabelItem labelItem = item as LabelItem;
                if (labelItem != null)
                {

                    string username = (labelItem.Label.OwnerName != null) ? labelItem.Label.OwnerName : String.Empty;
                    if (username.IndexOf(@"\") != -1)
                        username = username.Substring(username.IndexOf(@"\") + 1);

                    string comment = (labelItem.Label.Comment != null) ? labelItem.Label.Comment : String.Empty;
                    comment = comment.Replace(System.Environment.NewLine, " ");

                    LabelStub stub = new LabelStub();
                    stub.ID = labelItem.Label.LabelId;
                    stub.Name = labelItem.Label.Name;
                    stub.Comment = comment;
                    stub.Date = labelItem.Label.LastModifiedDate.ToShortDateString() + " " + labelItem.Label.LastModifiedDate.ToShortTimeString();

                    List<int> changesets = new List<int>();
                    foreach (Item relatedItem in labelItem.Label.Items)
                    {
                        changesets.Add(relatedItem.ChangesetId);
                    }
                    stub.Changesets = changesets;

                    AppendLabelStubs(stub);
                }
            }
        }
        #endregion
    }
}